#include "MatTest.h"

using physicsworks::operator<<;
using physicsworks::math::equal;

CPPUNIT_TEST_SUITE_REGISTRATION(MatTest);

void MatTest::setUp() {
	
	af55 = new Mat<float>(5,5);
	bf55 = new Mat<float>(5,5);
	af66 = new Mat<float>(6,6);
	bf66 = new Mat<float>(6,6);
	
	(*af55)(0,0) = 0; (*af55)(0,1) = 2; (*af55)(0,2) = 3; (*af55)(0,3) = 2; (*af55)(0,4) = 4;
	(*af55)(1,0) = 5; (*af55)(1,1) = 2; (*af55)(1,2) = 7; (*af55)(1,3) = 0; (*af55)(1,4) = 5;
	(*af55)(2,0) = 4; (*af55)(2,1) = 5; (*af55)(2,2) = 3; (*af55)(2,3) = 3; (*af55)(2,4) = 7;
	(*af55)(3,0) = 2; (*af55)(3,1) = 4; (*af55)(3,2) = 8; (*af55)(3,3) = 4; (*af55)(3,4) = 2;
	(*af55)(4,0) = 8; (*af55)(4,1) = 3; (*af55)(4,2) = 3; (*af55)(4,3) = 5; (*af55)(4,4) = 2;
	
	(*bf55)(0,0) = 0; (*bf55)(0,1) = 2; (*bf55)(0,2) = 3; (*bf55)(0,3) = 2; (*bf55)(0,4) = 4;
	(*bf55)(1,0) = 5; (*bf55)(1,1) = 2; (*bf55)(1,2) = 7; (*bf55)(1,3) = 0; (*bf55)(1,4) = 5;
	(*bf55)(2,0) = 4; (*bf55)(2,1) = 5; (*bf55)(2,2) = 3; (*bf55)(2,3) = 3; (*bf55)(2,4) = 7;
	(*bf55)(3,0) = 2; (*bf55)(3,1) = 4; (*bf55)(3,2) = 8; (*bf55)(3,3) = 4; (*bf55)(3,4) = 2;
	(*bf55)(4,0) = 8; (*bf55)(4,1) = 3; (*bf55)(4,2) = 3; (*bf55)(4,3) = 5; (*bf55)(4,4) = 2;
	
	ad55 = new Mat<double>(5,5);
	bd55 = new Mat<double>(5,5);
	ad66 = new Mat<double>(6,6);
	bd66 = new Mat<double>(6,6);
	
	(*ad55)(0,0) = 0; (*ad55)(0,1) = 2; (*ad55)(0,2) = 3; (*ad55)(0,3) = 2; (*ad55)(0,4) = 4;
	(*ad55)(1,0) = 5; (*ad55)(1,1) = 2; (*ad55)(1,2) = 7; (*ad55)(1,3) = 0; (*ad55)(1,4) = 5;
	(*ad55)(2,0) = 4; (*ad55)(2,1) = 5; (*ad55)(2,2) = 3; (*ad55)(2,3) = 3; (*ad55)(2,4) = 7;
	(*ad55)(3,0) = 2; (*ad55)(3,1) = 4; (*ad55)(3,2) = 8; (*ad55)(3,3) = 4; (*ad55)(3,4) = 2;
	(*ad55)(4,0) = 8; (*ad55)(4,1) = 3; (*ad55)(4,2) = 3; (*ad55)(4,3) = 5; (*ad55)(4,4) = 2;
	
	(*bd55)(0,0) = 0; (*bd55)(0,1) = 2; (*bd55)(0,2) = 3; (*bd55)(0,3) = 2; (*bd55)(0,4) = 4;
	(*bd55)(1,0) = 5; (*bd55)(1,1) = 2; (*bd55)(1,2) = 7; (*bd55)(1,3) = 0; (*bd55)(1,4) = 5;
	(*bd55)(2,0) = 4; (*bd55)(2,1) = 5; (*bd55)(2,2) = 3; (*bd55)(2,3) = 3; (*bd55)(2,4) = 7;
	(*bd55)(3,0) = 2; (*bd55)(3,1) = 4; (*bd55)(3,2) = 8; (*bd55)(3,3) = 4; (*bd55)(3,4) = 2;
	(*bd55)(4,0) = 8; (*bd55)(4,1) = 3; (*bd55)(4,2) = 3; (*bd55)(4,3) = 5; (*bd55)(4,4) = 2;
	
	m0f = new Mat<float>(2, 3);
	(*m0f)(0, 0) = 5; (*m0f)(0, 1) = 4; (*m0f)(0, 2) = 5;
	(*m0f)(1, 0) = 2; (*m0f)(1, 1) = 1; (*m0f)(1, 2) = 4;
	
	m1f = new Mat<float>(3, 4);
	(*m1f)(0, 0) = 5; (*m1f)(0, 1) = 2; (*m1f)(0, 2) = 5; (*m1f)(0, 3) = 2;
	(*m1f)(1, 0) = 0; (*m1f)(1, 1) = 3; (*m1f)(1, 2) = 6; (*m1f)(1, 3) = 1;
	(*m1f)(2, 0) = 2; (*m1f)(2, 1) = 1; (*m1f)(2, 2) = 0; (*m1f)(2, 3) = 1;
	
	m0d = new Mat<double>(2, 3);
	(*m0d)(0, 0) = 5; (*m0d)(0, 1) = 4; (*m0d)(0, 2) = 5;
	(*m0d)(1, 0) = 2; (*m0d)(1, 1) = 1; (*m0d)(1, 2) = 4;
	
	m1d = new Mat<double>(3, 4);
	(*m1d)(0, 0) = 5; (*m1d)(0, 1) = 2; (*m1d)(0, 2) = 5; (*m1d)(0, 3) = 2;
	(*m1d)(1, 0) = 0; (*m1d)(1, 1) = 3; (*m1d)(1, 2) = 6; (*m1d)(1, 3) = 1;
	(*m1d)(2, 0) = 2; (*m1d)(2, 1) = 1; (*m1d)(2, 2) = 0; (*m1d)(2, 3) = 1;
}

void MatTest::testSolver()
{
	Vec<float> sol0f(2);
	Vec<double> sol0d(2);
	CPPUNIT_ASSERT(m0f->solve(sol0f)); CPPUNIT_ASSERT(m0d->solve(sol0d));
	
	CPPUNIT_ASSERT( equal<float>( sol0f(0) * (*m0f)(0, 0) + sol0f(1) * (*m0f)(0, 1), (*m0f)(0, 2) ) );
	CPPUNIT_ASSERT( equal<float>( sol0f(0) * (*m0f)(1, 0) + sol0f(1) * (*m0f)(1, 1), (*m0f)(1, 2) ) );
	
	CPPUNIT_ASSERT( equal<double>( sol0d(0) * (*m0d)(0, 0) + sol0d(1) * (*m0d)(0, 1), (*m0d)(0, 2) ) );
	CPPUNIT_ASSERT( equal<double>( sol0d(0) * (*m0d)(1, 0) + sol0d(1) * (*m0d)(1, 1), (*m0d)(1, 2) ) );
	

	Vec<float> sol1f(3);
	Vec<double> sol1d(3);
	CPPUNIT_ASSERT(m1f->solve(sol1f)); CPPUNIT_ASSERT(m1d->solve(sol1d));
	
	CPPUNIT_ASSERT( equal<float>( sol1f(0) * (*m1f)(0, 0) + sol1f(1) * (*m1f)(0, 1) + sol1f(2) * (*m1f)(0, 2), (*m1f)(0, 3) ) );
	CPPUNIT_ASSERT( equal<float>( sol1f(0) * (*m1f)(1, 0) + sol1f(1) * (*m1f)(1, 1) + sol1f(2) * (*m1f)(1, 2), (*m1f)(1, 3) ) );
	CPPUNIT_ASSERT( equal<float>( sol1f(0) * (*m1f)(2, 0) + sol1f(1) * (*m1f)(2, 1) + sol1f(2) * (*m1f)(2, 2), (*m1f)(2, 3) ) );
	
	CPPUNIT_ASSERT( equal<double>( sol1d(0) * (*m1d)(0, 0) + sol1d(1) * (*m1d)(0, 1) + sol1d(2) * (*m1d)(0, 2), (*m1d)(0, 3) ) );
	CPPUNIT_ASSERT( equal<double>( sol1d(0) * (*m1d)(1, 0) + sol1d(1) * (*m1d)(1, 1) + sol1d(2) * (*m1d)(1, 2), (*m1d)(1, 3) ) );
	CPPUNIT_ASSERT( equal<double>( sol1d(0) * (*m1d)(2, 0) + sol1d(1) * (*m1d)(2, 1) + sol1d(2) * (*m1d)(2, 2), (*m1d)(2, 3) ) );
}

void MatTest::testEquality()
{
	CPPUNIT_ASSERT( *af55 == *bf55 );
}

void MatTest::testDeterminant()
{
	CPPUNIT_ASSERT( equal<double>(ad55->determinant(), 2748.0) );
}

void MatTest::testSize()
{
	CPPUNIT_ASSERT( af55->m() == af55->n() && af55->n() == 5 );
}

void MatTest::testReturningRowsCols()
{
	Vec<float> v0 = af55->column(2);
	Vec<float> v1 = bf55->column(2);
	CPPUNIT_ASSERT( v0 == v1 );
	
	v0 = af55->row(4);
	v1 = bf55->row(4);
	CPPUNIT_ASSERT( v0 == v1 );
}

void MatTest::tearDown()
{
	delete af55;
	delete bf55;
	delete af66;
	delete bf66;
	
	delete ad55;
	delete bd55;
	delete ad66;
	delete bd66;
	
	delete m0f;
	delete m1f;
	
	delete m0d;
	delete m1d;
}
